#include <iostream>
#include <functional>
#include <unordered_map>
#include <sys/poll.h>
#include "Sock.hpp"
#include "Epoll.hpp"
#include "Protocal.hpp"
#include "Util.hpp"

using namespace protocal_ns;

static const int N = 62; //_env的默认大小

static const uint16_t default_port = 8080; // 服务器默认端口号

using func_t = std::function<Reponse(const Request &)>; // 服务器处理函数

class Connection;
using callback = std::function<void(Connection *)>; // 回调函数

class Connection
{
public:
    Connection(int fd, const std::string &clientIp, const uint16_t clientPort)
        : _fd(fd), _clientIp(clientIp), _clientPort(clientPort)
    {
    }

    void Register(callback recver, callback sender, callback excepter) // 注册函数
    {
        _sender = sender;
        _recver = recver;
        _excepter = excepter;
    }

    ~Connection()
    {
    }

public:
    int _fd;
    std::string _inbuffer;  // 输入缓冲区
    std::string _outbuffer; // 输出缓冲区

    callback _sender;   // 发送函数
    callback _recver;   // 接收函数
    callback _excepter; // 异常函数

    std::string _clientIp; // Ip
    uint16_t _clientPort;  // 端口号

    uint32_t _event; // 事件
};

class EpollServer
{
public:
    EpollServer(uint16_t serverPort, func_t func)
        : _serverPort(serverPort), _func(func)
    {
    }

    void Init()
    {
        _sock.Socket();
        _sock.Bind(_serverPort);
        _sock.Listen();

        _epoll.creat();
        AddConnection(_sock.Fd(), EPOLLIN | EPOLLET);
    }

    void Dispatcher() // 事件派发器
    {
        int timeout = 1000;
        while (true)
        {
            LoopOnce(timeout);
        }
    }

    void LoopOnce(int timeout)
    {
        int n = _epoll.wait(_env, N, timeout);
        if (n == 0)
            LogMessage(Debug, "timeout");
        for (int i = 0; i < n; ++i)
        {
            if ((_env[i].events & EPOLLERR) || _env[i].events & EPOLLHUP) // 所有异常处理成读写异常
                _env[i].events |= (EPOLLIN | EPOLLOUT);

            if (_env[i].events & EPOLLIN) // 处理读事件
                _Connections[_env[i].data.fd]->_recver(_Connections[_env[i].data.fd]);
            else if (_env[i].events | EPOLLOUT) // 处理写事件
                _Connections[_env[i].data.fd]->_sender(_Connections[_env[i].data.fd]);
        }
    }

    void AddConnection(int fd, uint32_t event, std::string clientIp = "127.0.0.1", uint16_t clientPort = default_port)
    {
        if (event & EPOLLET) // 如果是边缘触发模式，就将fd设置为非阻塞
            Util::SetNonBlock(fd);

        Connection *con = new Connection(fd, clientIp, clientPort); // 创建fd的Connection对象
        if (fd == _sock.Fd())                                       // 根据不同的fd,设置回调函数
        {
            con->Register(bind(&EpollServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        }
        else
        {
            con->Register(std::bind(&EpollServer::Recver, this, std::placeholders::_1),
                          std::bind(&EpollServer::Sender, this, std::placeholders::_1),
                          std::bind(&EpollServer::Excepter, this, std::placeholders::_1));
        }
        con->_event = event;                           // 添加事件
        _Connections.insert(std::make_pair(fd, con));  // 添加到Connections里
        _epoll.AddMoveEvent(fd, event, EPOLL_CTL_ADD); // 添加到epoll里的红黑树结构，作为关心的fd&&event
        LogMessage(Debug, "AddConnection success");
    }

    void Accepter(Connection *con)
    {
        do
        {
            std::string clientIp;
            uint16_t clientPort;
            int sock = _sock.Accept(&clientIp, &clientPort);
            if (sock > 0)
            {
                LogMessage(Debug, "%s-%d accept success", clientIp.c_str(), clientPort); // 注意，在EpollServer.hpp的Accepter函数中，第一次获取链接成功，sock大于0
                AddConnection(sock, EPOLLIN | EPOLLET, clientIp, clientPort);            // 第二次获取链接失败，sock小于0，此时，errno为11，表示连接没有准备好，才会跳出
            }                                                                            // Accepter函数里面的循环，所以，sock.hpp中的Accept函数里面的sock小于0，不能直接终止
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK) // 读取未完毕
                {
                    break;
                }
                else if (errno == EINTR) // 信号中断
                {
                    continue;
                }
                else
                {
                    LogMessage(Warning, "accept fail,%d,%s", errno, strerror(errno));
                    continue;
                }
            }
        } while (con->_event & EPOLLET);
    }

    void HandlerRequest(Connection *con)
    {
        bool ret = false;
        while (!ret)
        {
            std::string package;
            int n = ParsePackage(con->_inbuffer, &package);
            if (n > 0)
            {
                package = RemoveHeader(package, n); // 去除报头
                Request req;
                req.DeSerialize(package); // 反序列化
                Reponse rep = _func(req); // 处理报文
                std::string message;
                rep.Serialize(&message);      // 序列化
                message = AddHeader(message); // 添加报头
                con->_outbuffer += message;
            }
            else
            {
                ret = true;
            }
        }
    }

    bool RecverHelper(Connection *con)
    {
        bool ret = true;
        do
        {
            char buffer[1024];
            int n = recv(con->_fd, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = '\0';
                con->_inbuffer += buffer;
            }
            else if (n == 0)
            {
                LogMessage(Debug, "%s-%d exit", con->_clientIp.c_str(), con->_clientPort);
                con->_excepter(con);
                ret = false;//注意要把ret置为false，不然Recver函数会调用HandlerRequest，对con进行操作，而con已经被释放了，导致段错误
                break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    con->_excepter(con);
                    ret = false;
                    break;
                }
            }
        } while (con->_event & EPOLLET);

        return ret;
    }

    void Recver(Connection *con)
    {
        if (!RecverHelper(con))
            return;
        HandlerRequest(con);
        if (!con->_outbuffer.empty())
            con->_sender(con); // 一般我们在面对写入的时候，直接写入，没写完，才交给epoll！
    }

    void Sender(Connection *con)
    {
        bool ret = true;
        do
        {
            int n = send(con->_fd, con->_outbuffer.c_str(), con->_outbuffer.size(), 0);
            if (n > 0)
            {
                con->_outbuffer.erase(0, n);
                if (con->_outbuffer.empty())
                    break;
            }
            else
            {

                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    con->_excepter(con);
                    ret = false;
                    break;
                }
            }
        } while (con->_event & EPOLLET);
        if (!ret)
            return;
        if (!con->_outbuffer.empty())
            EnableReadWrite(con, true, true);
        else
            EnableReadWrite(con, true, false);
    }

    void Excepter(Connection *con)
    {
        _epoll.DeleteEvent(con->_fd); // 从epoll中移除fd
        _Connections.erase(con->_fd); // 从_Connections移除
        close(con->_fd);              // 关闭文件描述符
        delete con;                   // 释放con
    }

    void EnableReadWrite(Connection *con, bool ReadAnble, bool WriteAnble)
    {
        con->_event |= ((ReadAnble ? EPOLLIN : 0) | (WriteAnble ? EPOLLOUT : 0) | EPOLLET);
        _epoll.AddMoveEvent(con->_fd, con->_event, EPOLL_CTL_MOD);
    }

    ~EpollServer()
    {
        _sock.Close();
        _epoll.Close();
    }

private:
    Sock _sock;
    Epoll _epoll;
    uint16_t _serverPort;
    epoll_event _env[N];
    func_t _func;
    std::unordered_map<int, Connection *> _Connections;
};